
#ifndef SX_PICKING_CONTROLLER_H
#define SX_PICKING_CONTROLLER_H

#include <sxMath.h>
#include <sxController.h>

namespace Sx {
namespace Graphics {
namespace Picking {

/*
 * The PickingController class provides an interface for projecting and 
 * un-projecting points between two and three dimensions based on the provided
 * camera implementation. The assumption is that the camera implementation
 * provides a viewing plane defined in two dimensions and that it also provides
 * the specification for mapping the 2D viewing plane to the 3D scene.
 *
 * This class can be implemented either by using the abstract definition of the
 * camera class or it can be implemented by using additional information from
 * the state of the running graphics context.
 */
template <typename CameraType>
class PickingController : public Controller<CameraType> {
public:
	PickingController(float extent = 100.0f);
	virtual ~PickingController();

	/* 
	 * General construct function. An implementation may need to initialize
	 * some internal members. if this is the case, the derived class can
	 * use the required implementation; otherwise this function implementation
	 * can be a return statement.
	 */
	virtual bool construct() = 0;

	/* 
	 * General update function. An implementation class may or may not need
	 * to update its state to provide the proper picking information.
	 */
	virtual bool update() = 0;

	/*
	 * Mapping function for translating a 3D point to the viewing plane of the
	 * provided camera. The worldPoint specifies the point in 3D space to be
	 * projected and the viewPoint represents the position of that point on
	 * the camera's viewing plane.
	 */
	virtual bool project(const CameraType* const camera, const Eigen::Vector3f& worldPoint, Eigen::Vector2i& viewPoint) = 0;

	/*
	 * Mapping function for translation of 2D points to a three dimensional 
	 * origin and direction vector (origin is embedded in the cameras viewing
	 * plane and the direction is based on the projection of the camera).
	 */
	virtual bool unproject(const CameraType* const camera, int x, int y, Eigen::Vector3f& origin, Eigen::Vector3f& direction) = 0;

	/* Performs the same operation as the unproject function */
	virtual bool createPickRay(const CameraType* const camera, int x, int y, Eigen::Vector3f& start, Eigen::Vector3f& end) = 0;

	/* Set/get the length of the picking ray that will be generated. */
	void setExtent(float extent);
	float getExtent() const;

protected:
	/*
	 * The extent should define the length of the picking ray that is generated
	 * by the unproject function.
	 */
	float extent;
};

}

}

}

/* 
 * The user is not in charge of providing the camera type so this type of
 * controller does not require an internal pointer.
 */
template <typename CameraType>
Sx::Graphics::Picking::PickingController<CameraType>::PickingController(float extent) : Controller(nullptr) {
	this->extent = extent;
}

template <typename CameraType>
Sx::Graphics::Picking::PickingController<CameraType>::~PickingController() {}

template <typename CameraType>
void Sx::Graphics::Picking::PickingController<CameraType>::setExtent(float extent) {
	this->extent = extent;
}

template <typename CameraType>
float Sx::Graphics::Picking::PickingController<CameraType>::getExtent() const {
	return this->extent;
}

#endif
